﻿using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using TankServer.Logic;

namespace TankServer.Core
{
    class ServNet
    {
        public Socket listenfd;
        public Conn[] conns;
        public int maxConn = 50;
        public static ServNet instance;
        public MySqlConnection sqlConn;

        Timer timer = new Timer(1000);
        public long heratBeatTime = 180;

        public ProtocolBase proto;

        //消息分发
        public HandleConnMsg handleConnMsg = new HandleConnMsg();
        public HandlePlayerEvent handlePlayerEvent = new HandlePlayerEvent();
        public HandlePlayerMsg handlePlayerMsg = new HandlePlayerMsg();

        public ServNet()
        {
            instance = this;
        }
        //获取连接池(conns数组)的索引，返回附属表示获取失败
        public int NewIndex()
        {
            if (conns == null)
            {
                return -1;
            }
            for (int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)
                {
                    conns[i] = new Conn();
                    return i;
                }
                else if (conns[i].isUse == false)
                {
                    return i;
                }
            }
            return -1;
        }

        public void Start(string host, int port)
        {
            timer.AutoReset = false;
            timer.Enabled = true;
            timer.Elapsed += new ElapsedEventHandler(HandleMainTimer);

            //初始化连接池
            conns = new Conn[maxConn];
            for (int i = 0; i < conns.Length; i++)
            {
                conns[i] = new Conn();
            }
            listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipAdr = IPAddress.Parse(host);
            IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
            listenfd.Bind(ipEp);
            listenfd.Listen(maxConn);
            listenfd.BeginAccept(AcceptCb, null);
            Console.WriteLine("[服务器]启动成功！");
        }

        private void AcceptCb(IAsyncResult ar)
        {
            try
            {
                Socket socket = listenfd.EndAccept(ar);
                int index = NewIndex();
                if (index < 0)
                {
                    socket.Close();
                    Console.WriteLine("[警告]连接已满！");
                }
                else
                {
                    Conn conn = conns[index];
                    conn.Init(socket);
                    string adr = conn.GetAddress();
                    Console.WriteLine("客户端连接[" + adr + "]连接池connsID：" + index);
                    conn.socket.BeginReceive(conn.readBuffer, conn.buffcount, conn.BufferRemain(), SocketFlags.None, ReceiveCb, conn);
                    listenfd.BeginAccept(AcceptCb, null);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("AcceptCb失败" + e.Message);
            }
        }
        private void ReceiveCb(IAsyncResult ar)
        {
            Conn conn = (Conn)ar.AsyncState;
            try
            {
                int count = conn.socket.EndReceive(ar);
                if (count <= 0)
                {
                    Console.WriteLine("收到[" + conn.GetAddress() + "]断开连接");
                    conn.Close();
                    return;
                }
                conn.buffcount += count;
                ProcessData(conn);

                //继续接收
                conn.socket.BeginReceive(conn.readBuffer, conn.buffcount, conn.BufferRemain(), SocketFlags.None, ReceiveCb, conn);
            }
            catch (Exception e)
            {
                Console.WriteLine("收到[" + conn.GetAddress() + "]断开连接!" + e.Message);
                conn.Close();
            }
        }

        public void Close()
        {
            for(int i = 0; i < conns.Length; i++)
            {
                Conn conn = conns[i];
                if(conn == null)
                {
                    continue;
                }
                if (!conn.isUse)
                {
                    continue;
                }
            }
        }

        private void ProcessData(Conn conn)
        {
            if(conn.buffcount < sizeof(Int32))
            {
                return;
            }
            //
            Array.Copy(conn.readBuffer,conn.lenBytes,sizeof(Int32));
            conn.msgLength = BitConverter.ToInt32(conn.lenBytes,0);
            if(conn.buffcount < conn.msgLength + sizeof(Int32))
            {
                return;
            }
            //处理消息
            ProtocolBase protocol = proto.Decode(conn.readBuffer,sizeof(Int32), conn.msgLength);
            HandleMsg(conn,protocol);

            //发送数据
            int count = conn.buffcount - conn.msgLength - sizeof(Int32);
            Array.Copy(conn.readBuffer,sizeof(Int32)+conn.msgLength,conn.readBuffer,0,count);
            conn.buffcount = count;
            if(conn.buffcount > 0)
            {
                ProcessData(conn);
            }
        }

        private void HandleMsg(Conn conn,ProtocolBase protoBase)
        {
            string name = protoBase.GetName();
            string methodName = "Msg" + name;

            if(conn.player == null||name == "HeatBeat" || name == "Logout")
            {
                MethodInfo mm = handleConnMsg.GetType().GetMethod(methodName);
                if(mm == null)
                {
                    string str = "[警告]handleMsg没有连接处理方法";
                    Console.WriteLine(str+methodName);
                    return;
                }

                Object[] obj = new object[] {conn,protoBase};
                Console.WriteLine("[处理连接消息]"+conn.GetAddress()+name);
                mm.Invoke(handleConnMsg,obj);

            }
            else
            {
                MethodInfo mm = handlePlayerMsg.GetType().GetMethod(methodName);
                if (mm == null)
                {
                    string str = "[警告]handleMsg没有处理玩家方法";
                    Console.WriteLine(str + methodName);
                    return;
                }

                Object[] obj = new object[] { conn.player, protoBase };
                Console.WriteLine("[处理玩家消息]" + conn.GetAddress() + name);
                mm.Invoke(handlePlayerMsg, obj);
            }
        }

        public void Send(Conn conn,ProtocolBase protocol)
        {
            byte[] bytes = protocol.Encode();
            byte[] length = BitConverter.GetBytes(bytes.Length);
            byte[] sendbuff = length.Concat(bytes).ToArray();

            try
            {
                conn.socket.BeginSend(sendbuff,0,sendbuff.Length,SocketFlags.None,null,null);
            }catch(Exception e)
            {
                Console.WriteLine("[发送消息]"+conn.GetAddress()+":"+e.Message);
            }
        }

        public void Broadcast(ProtocolBase protocol)
        {
            for(int i = 0; i < conns.Length; i++)
            {
                if (!conns[i].isUse)
                {
                    continue;
                }
                if (conns[i] == null)
                {
                    continue;
                }
                Send(conns[i],protocol);
            }
        }

        public void HandleMainTimer(object sender,ElapsedEventArgs e)
        {
            //处理心跳
            HeartBeatTime();
            timer.Start();
        }
        public void HeartBeatTime()
        {
            long timeNow = Sys.GetTimeStamp();
            for(int i = 0; i < conns.Length; i++)
            {
                Conn conn = conns[i];
                if (conn == null) continue;
                if (!conn.isUse) continue;
                if(conn.lastTickTime < timeNow - heratBeatTime)
                {
                    Console.WriteLine("[心跳引起断开连接]"+conn.GetAddress());
                    lock (conn)
                    {
                        conn.Close();
                    }
                }
            }
        }

        public void Print()
        {
            Console.WriteLine("=====服务器登录信息====");
            for(int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)
                {
                    continue;
                }
                if (!conns[i].isUse)
                {
                    continue;
                }
                string str = "连接[" + conns[i].GetAddress() + "]";
                if(conns[i].player != null)
                {
                    str += "玩家id" + conns[i].player.id;
                }
                Console.WriteLine(str);
            }
        }
    }
}
